package figure8;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Mixer;
import javax.swing.JFileChooser;

import figure8.Project.InvalidProjectException;
import figure8.GUI.EffectWindow;
import figure8.GUI.ProjectAlreadyExistsWindow;
import figure8.GUI.SeqMainWindow;
import figure8.GUI.SeqSaveWindow;

public class Seq {
	
	private Controller controller;
	private ArrayList<Project> projects;
	private DrumMachineTrack drumTrack;
	private ArrayList<Boolean[][]> drums;
	private Mixer mixerPlay;
	private static AudioFormat format;
	private static DataLine.Info info;
	private static float sampleRates[] = {44100, 16000, 11025, 8000};
	private boolean drumDone;
	private SeqMainWindow window;

	private boolean loaded;
	private File directory;
	
	public Seq (Controller controller) {
		loaded = false;
		projects= new ArrayList<Project>();
		drums = new ArrayList<Boolean[][]>();
		this.controller = controller;
		Mixer.Info[] mixers = AudioSystem.getMixerInfo();
		Pattern pattern = null;
		drumDone = false;
		Matcher matcher;
		int mixerSelector = 0;
		int count = 0;
		for ( Mixer.Info mix : Rec.getOutputMixers())
		{
			pattern = pattern.compile("Java Sound Audio Engine");
			matcher = pattern.matcher(mix.toString());
			if (matcher.find()){
				mixerSelector = count;
			}
			count++;
		}
		float sampleRate = 16000.0F;
		int sampleSizeInBits = 16;
		int channels = 1;
		boolean signed = true;
		boolean bigEndian = false;
		mixerPlay = AudioSystem.getMixer(Rec.getOutputMixers()[mixerSelector]);
		format = new AudioFormat(sampleRate, sampleSizeInBits, channels,
				signed, bigEndian);
		info = new DataLine.Info(Clip.class, format);
		window = new SeqMainWindow(this.controller, this);
	}
	
	public Seq (Controller controller, DrumMachineTrack drum) {
		loaded = false;
		projects= new ArrayList<Project>();
		drums = new ArrayList<Boolean[][]>();
		this.controller = controller;
		Mixer.Info[] mixers = AudioSystem.getMixerInfo();
		Pattern pattern = null;
		if (drum != null) {
			drumDone = true;
			drumTrack = drum;
		} else drumDone = false;
		Matcher matcher;
		int mixerSelector = 0;
		int count = 0;
		for ( Mixer.Info mix : Rec.getOutputMixers())
		{
			pattern = pattern.compile("Java Sound Audio Engine");
			matcher = pattern.matcher(mix.toString());
			if (matcher.find()){
				mixerSelector = count;
			}
			count++;
		}
		float sampleRate = 16000.0F;
		int sampleSizeInBits = 16;
		int channels = 1;
		boolean signed = true;
		boolean bigEndian = false;
		mixerPlay = AudioSystem.getMixer(Rec.getOutputMixers()[mixerSelector]);
		format = new AudioFormat(sampleRate, sampleSizeInBits, channels,
				signed, bigEndian);
		info = new DataLine.Info(Clip.class, format);
		window = new SeqMainWindow(this.controller, this);
	}


	
	class PlayBack extends Thread {
		public void run() {
			
			for (int i = 0; i < projects.size(); i++) {
				if (i > 0) {
					projects.get(i - 1).stop();
					drumTrack.stop();
				}
				//window.showCurrentLoop(i);
				drumTrack.switchProject(projects.get(i).getTiming(), drums.get(i));
				projects.get(i).play();
				drumTrack.playBack();
				window.nextLoop();
			}
			window.stopped();
		}
	}
	
	public void close() {
		if (projects.size() > 0) drumTrack.closeLines();
		for (int i = 0; i < projects.size(); i++) {
			projects.get(i).closeLines();
		}
		controller.getWindow().removeTrayIcon();
		window.dispose();
		Controller newController = new Controller();
	}
	
	public void play() {
		PlayBack playBack = new PlayBack();
		playBack.start();
	}
	
	public void deleteLoop(Project project) {
		int index = projects.indexOf(project);
		projects.remove(project);
		drums.remove(index);
	}
	
	public void stop() {
		for (int i = 0; i < drums.size(); i++) {
			projects.get(i).stop();
			drumTrack.stop();
		}
	}
	
	public int indexOfProject(Project project) {
		return projects.indexOf(project);
	}
	
	public String getLastProjectName() {
		return projects.get(projects.size() - 1).getName();
	} 
	
	public String getProjectName(int index) {
		return projects.get(index).getName();
	} 
	
	public Project getLastProject(){
		return projects.get(projects.size() -1);
	}
	
	public void projectBuilder (String dir) {
		File directory = new File(dir);
		Project project;
		try {
			project = new Project(directory, this.controller);
			ArrayList<File> filesKeep = new ArrayList<File>();
			try {
				BufferedReader reader = new BufferedReader(new FileReader(new File(directory + 
						File.separator + "list_tracks.f8p")));
				ObjectInputStream stream = null;
				String nextLine;
				while((nextLine = reader.readLine()) != null) {
					nextLine.trim();
					StringTokenizer recVarTok = new StringTokenizer(nextLine, ",");
					String nextFile = recVarTok.nextToken();
					float vol = Float.parseFloat(recVarTok.nextToken());
					File trackFile;
					if(nextFile.startsWith(
							directory.getAbsolutePath()) == false) {
						int index = nextFile.lastIndexOf(File.separator);
						String newPath = directory.getAbsolutePath() + File.separator;
						if(nextFile.endsWith("drum_machine.f8p") == false) {
							newPath = newPath + "Recordings" + File.separator;
						}
						newPath = newPath + nextFile.substring(index);
						trackFile = new File(newPath);
					}
					else {
						trackFile = new File(nextFile);
					}
					if(trackFile.getAbsolutePath().endsWith("drum_machine.f8p")) {
						stream = new ObjectInputStream(new FileInputStream(trackFile));
						Boolean[][] drum = (Boolean[][]) stream.readObject();
						drums.add(drum);
						if(!drumDone) {
							drumTrack = new DrumMachineTrack(project.getTiming(), drum);
							int kitNum = Integer.parseInt(recVarTok.nextToken());
							drumTrack.changeKit(kitNum);
							drumTrack.setVolume(vol);
							drumDone = true;
						}
						stream.close();
					}
					else {
						filesKeep.add(trackFile);
						if(trackFile.getAbsolutePath().endsWith(".wav")) {
							WAVTrack track = new WAVTrack((Clip) 
									mixerPlay.getLine(info),trackFile, project.getTiming());
							track.setVolume(vol);
							if(recVarTok.hasMoreTokens()) {
								String eff = recVarTok.nextToken();
								track.setEffChain(eff);
								new EffectWindow(project.getTiming(), track, eff.split(";"));
							}
							System.out.println("File added: " + trackFile.getName());
							project.addTrack(track);			
						}
						else {
							MIDITrack track = new MIDITrack(trackFile);
							track.setVolume(vol);
							System.out.println("File added: " + trackFile.getName());
							int instrumentNumber = Integer.parseInt(recVarTok.nextToken());
							//midiTrack.get(midiTrack.size() - 1).changeInstrument(this.instrumentNumber);
							track.changeInstrument(instrumentNumber);
							project.addTrack(track);
						}
					}
					
				}
				projects.add(project);
				reader.close();
			} catch (javax.sound.sampled.LineUnavailableException e) {
				System.err.println("Line available when giving to WAVTrack");
			} catch (MidiUnavailableException e) {
				e.printStackTrace();
			} catch (InvalidMidiDataException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (InvalidProjectException e1) {
			e1.printStackTrace();
		}
	}
	
	public ArrayList<Project> getSequence () {
		return projects;
	}
	
	public void save() {
		if(loaded == false) {
			SeqSaveWindow saveWin = new SeqSaveWindow(window, this);
		}
		else {
			saveFile(directory);
			loaded = true;
		}
	}
	
	public void saveFile(File projectDir) {
		directory = projectDir;
		ArrayList<Project> projs = this.getSequence();
		try {
			File seqFile = new File(projectDir.getAbsolutePath() + File.separator + "list_projects" + ".f8p");
			if(seqFile.exists()) {
				seqFile.delete();
			}
			seqFile.createNewFile();
			String newline = System.getProperty("line.separator");
			BufferedWriter out = new BufferedWriter(new FileWriter(seqFile));
			for(int i = 0; i<projs.size(); i++) {
				File location = projs.get(i).getLocation();
				out.write(location.getAbsolutePath());
				out.write(newline);
			}
			out.close();
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public void open() {
		loaded = true;
		JFileChooser openFC = new JFileChooser();
		openFC.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		int returnVal = openFC.showOpenDialog(window.getPanel());
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			String dir = openFC.getSelectedFile().getAbsolutePath();
			directory = new File(dir);
			
			try {
				BufferedReader reader = new BufferedReader(new FileReader(new File(dir + 
						File.separator + "list_projects.f8p")));
				String nextLine;
				while((nextLine = reader.readLine()) != null) {
					nextLine.trim();
					this.projectBuilder(nextLine);
					window.addLoopPane();
				}
				reader.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else
			System.out.println("Seq open window cancel button clicked");
	}
	
	public SeqMainWindow getWindow() {
		return window;
	}
}